package cn.sixmillions.wiki.common.handler;


import cn.sixmillions.wiki.common.enums.IResultCode;
import cn.sixmillions.wiki.common.enums.ResultCodeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;

/**
 * @description: 统一结果返回
 * @author: sixmillions
 * @since: 2021/06/04
 **/
@ApiModel(description = "返回信息统一封装")
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "状态码", required = true)
    private int code;

    @ApiModelProperty(value = "是否成功", required = true)
    private boolean success;

    @ApiModelProperty("承载数据")
    private T data;

    @ApiModelProperty(value = "返回消息", required = true)
    private String message;

    @ApiModelProperty(value = "发生时间", required = true)
    private Long time;

    @ApiModelProperty("错误代码")
    private String errorCode;

    private R(IResultCode resultCode) {
        this(resultCode, null, resultCode.getMessage());
    }

    private R(IResultCode resultCode, String message) {
        this(resultCode, null, message);
    }

    private R(IResultCode resultCode, T data) {
        this(resultCode, data, resultCode.getMessage());
    }

    private R(IResultCode resultCode, T data, String message) {
        this(resultCode.getCode(), data, message);
    }

    private R(IResultCode resultCode, T data, String errorCode, String message) {
        this(resultCode.getCode(), data, errorCode, message);
    }

    private R(int code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
        this.success = ResultCodeEnum.SUCCESS.getCode() == code;
        this.time = Instant.now().getEpochSecond();
    }

    private R(int code, T data, String errorCode, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
        this.success = ResultCodeEnum.SUCCESS.getCode() == code;
        this.time = Instant.now().getEpochSecond();
        this.errorCode = errorCode;
    }

    /**
     * code如果是200则返回true
     *
     * @param result
     * @return
     */
    public static boolean isSuccess(@Nullable R<?> result) {
        return Optional.ofNullable(result).map(x -> nullSafeEquals(ResultCodeEnum.SUCCESS.getCode(), x.code)).orElse(Boolean.FALSE);
    }

    /**
     * code如果不是200则返回true
     *
     * @param result
     * @return
     */
    public static boolean isNotSuccess(@Nullable R<?> result) {
        return !isSuccess(result);
    }

    public static <T> R<T> data(T data) {
        return data(data, "");
    }

    public static <T> R<T> data(T data, String msg) {
        return data(200, data, msg);
    }

    public static <T> R<T> data(int code, T data, String msg) {
        return new R(code, data, data == null ? "暂无承载数据" : msg);
    }

    public static <T> R<T> success(String msg) {
        return new R(ResultCodeEnum.SUCCESS, msg);
    }

    public static <T> R<T> success(IResultCode resultCode) {
        return new R(resultCode);
    }

    public static <T> R<T> success(IResultCode resultCode, String msg) {
        return new R(resultCode, msg);
    }

    public static <T> R<T> fail(String msg) {
        return new R(ResultCodeEnum.FAILURE, msg);
    }

    public static <T> R<T> fail(String errorCode, String msg) {
        return new R(ResultCodeEnum.FAILURE, (Object) null, errorCode, msg);
    }

    public static <T> R<T> fail(int code, String msg) {
        return new R(code, (Object) null, msg);
    }

    public static <T> R<T> fail(IResultCode resultCode) {
        return new R(resultCode);
    }

    public static <T> R<T> fail(IResultCode resultCode, String msg) {
        return new R(resultCode, msg);
    }

    public static <T> R<T> fail(IResultCode resultCode, String errorCode, String msg) {
        return new R(resultCode, (Object) null, errorCode, msg);
    }

    public static <T> R<T> status(boolean flag) {
        return flag ? success("") : fail("操作失败");
    }

    public int getCode() {
        return this.code;
    }

    public boolean isSuccess() {
        return this.success;
    }

    public T getData() {
        return this.data;
    }

    public String getMessage() {
        return this.message;
    }

    public Long getTime() {
        return this.time;
    }

    public String getErrorCode() {
        return this.errorCode;
    }

    public void setCode(final int code) {
        this.code = code;
    }

    public void setSuccess(final boolean success) {
        this.success = success;
    }

    public void setData(final T data) {
        this.data = data;
    }

    public void setMessage(final String message) {
        this.message = message;
    }

    public void setTime(final Long time) {
        this.time = time;
    }

    public void setErrorCode(final String errorCode) {
        this.errorCode = errorCode;
    }

    @Override
    public String toString() {
        return "R(code=" + this.getCode() + ", success=" + this.isSuccess() + ", data=" + this.getData() + ", message=" + this.getMessage() + ", time=" + this.getTime() + ", errorCode=" + this.getErrorCode() + ")";
    }

    public R() {
    }

    private static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
        if (o1 == o2) {
            return true;
        } else if (o1 != null && o2 != null) {
            if (o1.equals(o2)) {
                return true;
            } else {
                return (o1.getClass().isArray() && o2.getClass().isArray()) && arrayEquals(o1, o2);
            }
        } else {
            return false;
        }
    }

    private static boolean arrayEquals(Object o1, Object o2) {
        if (o1 instanceof Object[] && o2 instanceof Object[]) {
            return Arrays.equals((Object[]) ((Object[]) o1), (Object[]) ((Object[]) o2));
        } else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
            return Arrays.equals((boolean[]) ((boolean[]) o1), (boolean[]) ((boolean[]) o2));
        } else if (o1 instanceof byte[] && o2 instanceof byte[]) {
            return Arrays.equals((byte[]) ((byte[]) o1), (byte[]) ((byte[]) o2));
        } else if (o1 instanceof char[] && o2 instanceof char[]) {
            return Arrays.equals((char[]) ((char[]) o1), (char[]) ((char[]) o2));
        } else if (o1 instanceof double[] && o2 instanceof double[]) {
            return Arrays.equals((double[]) ((double[]) o1), (double[]) ((double[]) o2));
        } else if (o1 instanceof float[] && o2 instanceof float[]) {
            return Arrays.equals((float[]) ((float[]) o1), (float[]) ((float[]) o2));
        } else if (o1 instanceof int[] && o2 instanceof int[]) {
            return Arrays.equals((int[]) ((int[]) o1), (int[]) ((int[]) o2));
        } else if (o1 instanceof long[] && o2 instanceof long[]) {
            return Arrays.equals((long[]) ((long[]) o1), (long[]) ((long[]) o2));
        } else {
            return (o1 instanceof short[] && o2 instanceof short[]) && Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2));
        }
    }

}

